package com.onvif.manager.util;

import com.wondertek.onvif.entity.OnvifDevice;
import com.wondertek.onvif.service.OnvifClientService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 设备通道认证诊断工具
 * 用于诊断和修复Terminal#906-949类型的设备通道信息获取失败问题
 * 
 * @author ONVIF Manager
 * @version 1.0
 * @since 2024-12
 */
@Component
public class DeviceChannelAuthDiagnostic {
    
    private static final Logger log = LoggerFactory.getLogger(DeviceChannelAuthDiagnostic.class);
    
    @Autowired
    private OnvifClientService onvifClientService;
    
    /**
     * 诊断结果枚举
     */
    public enum DiagnosticResult {
        SUCCESS("诊断成功"),
        NETWORK_UNREACHABLE("网络不可达"),
        PORT_CLOSED("端口关闭"),
        AUTH_FAILED("认证失败"),
        ONVIF_SERVICE_UNAVAILABLE("ONVIF服务不可用"),
        TIMEOUT("连接超时"),
        UNKNOWN_ERROR("未知错误");
        
        private final String description;
        
        DiagnosticResult(String description) {
            this.description = description;
        }
        
        public String getDescription() {
            return description;
        }
    }
    
    /**
     * 诊断报告类
     */
    public static class DiagnosticReport {
        private String deviceId;
        private String deviceName;
        private String ipAddress;
        private int port;
        private DiagnosticResult result;
        private String errorMessage;
        private List<String> suggestions;
        private Map<String, Object> details;
        private LocalDateTime timestamp;
        
        public DiagnosticReport() {
            this.suggestions = new ArrayList<>();
            this.details = new HashMap<>();
            this.timestamp = LocalDateTime.now();
        }
        
        // Getters and Setters
        public String getDeviceId() { return deviceId; }
        public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
        
        public String getDeviceName() { return deviceName; }
        public void setDeviceName(String deviceName) { this.deviceName = deviceName; }
        
        public String getIpAddress() { return ipAddress; }
        public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
        
        public int getPort() { return port; }
        public void setPort(int port) { this.port = port; }
        
        public DiagnosticResult getResult() { return result; }
        public void setResult(DiagnosticResult result) { this.result = result; }
        
        public String getErrorMessage() { return errorMessage; }
        public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
        
        public List<String> getSuggestions() { return suggestions; }
        public void setSuggestions(List<String> suggestions) { this.suggestions = suggestions; }
        
        public Map<String, Object> getDetails() { return details; }
        public void setDetails(Map<String, Object> details) { this.details = details; }
        
        public LocalDateTime getTimestamp() { return timestamp; }
        public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
        
        public void addSuggestion(String suggestion) {
            this.suggestions.add(suggestion);
        }
        
        public void addDetail(String key, Object value) {
            this.details.put(key, value);
        }
        
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("=== 设备通道认证诊断报告 ===\n");
            sb.append("设备ID: ").append(deviceId).append("\n");
            sb.append("设备名称: ").append(deviceName).append("\n");
            sb.append("IP地址: ").append(ipAddress).append(":").append(port).append("\n");
            sb.append("诊断结果: ").append(result.getDescription()).append("\n");
            sb.append("诊断时间: ").append(timestamp.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).append("\n");
            
            if (errorMessage != null) {
                sb.append("错误信息: ").append(errorMessage).append("\n");
            }
            
            if (!suggestions.isEmpty()) {
                sb.append("\n修复建议:\n");
                for (int i = 0; i < suggestions.size(); i++) {
                    sb.append("  ").append(i + 1).append(". ").append(suggestions.get(i)).append("\n");
                }
            }
            
            if (!details.isEmpty()) {
                sb.append("\n详细信息:\n");
                details.forEach((key, value) -> 
                    sb.append("  ").append(key).append(": ").append(value).append("\n")
                );
            }
            
            return sb.toString();
        }
    }
    
    /**
     * 执行完整的设备通道认证诊断
     * 
     * @param device ONVIF设备
     * @return 诊断报告
     */
    public DiagnosticReport diagnoseDevice(OnvifDevice device) {
        log.info("开始诊断设备: {} ({}:{})", device.getName(), device.getIpAddress(), device.getPort());
        
        DiagnosticReport report = new DiagnosticReport();
        report.setDeviceId(String.valueOf(device.getId()));
        report.setDeviceName(device.getName());
        report.setIpAddress(device.getIpAddress());
        report.setPort(device.getPort());
        
        try {
            // 1. 网络连通性测试
            if (!testNetworkConnectivity(device, report)) {
                return report;
            }
            
            // 2. 端口连通性测试
            if (!testPortConnectivity(device, report)) {
                return report;
            }
            
            // 3. ONVIF服务可用性测试
            if (!testOnvifServiceAvailability(device, report)) {
                return report;
            }
            
            // 4. 认证测试
            if (!testAuthentication(device, report)) {
                return report;
            }
            
            // 5. 通道信息获取测试
            if (!testChannelInfoRetrieval(device, report)) {
                return report;
            }
            
            report.setResult(DiagnosticResult.SUCCESS);
            report.addSuggestion("设备通道认证诊断通过，所有测试项目正常");
            
        } catch (Exception e) {
            log.error("诊断过程中发生异常: {}", e.getMessage(), e);
            report.setResult(DiagnosticResult.UNKNOWN_ERROR);
            report.setErrorMessage(e.getMessage());
            report.addSuggestion("发生未知错误，建议检查系统日志获取详细信息");
        }
        
        log.info("设备诊断完成: {} - {}", device.getName(), report.getResult().getDescription());
        return report;
    }
    
    /**
     * 测试网络连通性
     */
    private boolean testNetworkConnectivity(OnvifDevice device, DiagnosticReport report) {
        log.debug("测试网络连通性: {}", device.getIpAddress());
        
        try {
            InetAddress address = InetAddress.getByName(device.getIpAddress());
            boolean reachable = address.isReachable(2000);
            
            report.addDetail("网络连通性", reachable ? "正常" : "失败");
            
            if (!reachable) {
                report.setResult(DiagnosticResult.NETWORK_UNREACHABLE);
                report.setErrorMessage("无法ping通设备IP地址: " + device.getIpAddress());
                report.addSuggestion("检查设备IP地址是否正确");
                report.addSuggestion("检查网络连接是否正常");
                report.addSuggestion("检查防火墙设置是否阻止了ICMP包");
                report.addSuggestion("尝试使用telnet测试端口连通性");
                return false;
            }
            
            return true;
            
        } catch (IOException e) {
            log.warn("网络连通性测试失败: {}", e.getMessage());
            report.setResult(DiagnosticResult.NETWORK_UNREACHABLE);
            report.setErrorMessage("网络连通性测试异常: " + e.getMessage());
            report.addSuggestion("检查设备IP地址格式是否正确");
            report.addSuggestion("检查DNS解析是否正常");
            return false;
        }
    }
    
    /**
     * 测试端口连通性
     */
    private boolean testPortConnectivity(OnvifDevice device, DiagnosticReport report) {
        log.debug("测试端口连通性: {}:{}", device.getIpAddress(), device.getPort());
        
        try (Socket socket = new Socket()) {
            socket.connect(new InetSocketAddress(device.getIpAddress(), device.getPort()), 2000);
            report.addDetail("端口连通性", "正常");
            return true;
            
        } catch (IOException e) {
            log.warn("端口连通性测试失败: {}", e.getMessage());
            report.setResult(DiagnosticResult.PORT_CLOSED);
            report.setErrorMessage("无法连接到设备端口: " + device.getIpAddress() + ":" + device.getPort());
            report.addSuggestion("检查设备端口号是否正确（通常为80或8080）");
            report.addSuggestion("检查设备是否已启动ONVIF服务");
            report.addSuggestion("检查设备防火墙设置");
            report.addSuggestion("尝试通过设备Web界面确认ONVIF服务状态");
            report.addDetail("端口连通性", "失败: " + e.getMessage());
            return false;
        }
    }
    
    /**
     * 测试ONVIF服务可用性
     */
    private boolean testOnvifServiceAvailability(OnvifDevice device, DiagnosticReport report) {
        log.debug("测试ONVIF服务可用性: {}", device.getIpAddress());
        
        try {
            // 构建GetCapabilities请求
            String soapRequest = buildGetCapabilitiesRequest();
            String serviceUrl = "http://" + device.getIpAddress() + ":" + device.getPort() + "/onvif/device_service";
            
            // 发送请求（不带认证）
            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                try {
                    return sendHttpRequest(serviceUrl, soapRequest, null, null);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
            
            String response = future.get(5, TimeUnit.SECONDS);
            
            if (response != null && (response.contains("GetCapabilitiesResponse") || response.contains("401"))) {
                report.addDetail("ONVIF服务", "可用");
                return true;
            } else {
                report.setResult(DiagnosticResult.ONVIF_SERVICE_UNAVAILABLE);
                report.setErrorMessage("ONVIF服务响应异常");
                report.addSuggestion("检查设备是否支持ONVIF协议");
                report.addSuggestion("确认ONVIF服务路径是否为/onvif/device_service");
                report.addSuggestion("尝试其他常见的ONVIF服务路径");
                report.addDetail("ONVIF服务", "不可用");
                return false;
            }
            
        } catch (Exception e) {
            log.warn("ONVIF服务可用性测试失败: {}", e.getMessage());
            report.setResult(DiagnosticResult.ONVIF_SERVICE_UNAVAILABLE);
            report.setErrorMessage("ONVIF服务测试异常: " + e.getMessage());
            report.addSuggestion("检查设备ONVIF服务是否正常启动");
            report.addSuggestion("确认设备固件版本是否支持ONVIF");
            report.addDetail("ONVIF服务", "测试异常: " + e.getMessage());
            return false;
        }
    }
    
    /**
     * 测试认证
     */
    private boolean testAuthentication(OnvifDevice device, DiagnosticReport report) {
        log.debug("测试设备认证: {}", device.getIpAddress());
        
        try {
            // 测试Basic认证
            boolean basicAuthSuccess = testBasicAuth(device, report);
            
            // 测试Digest认证
            boolean digestAuthSuccess = testDigestAuth(device, report);
            
            if (!basicAuthSuccess && !digestAuthSuccess) {
                report.setResult(DiagnosticResult.AUTH_FAILED);
                report.setErrorMessage("所有认证方式都失败");
                report.addSuggestion("检查用户名和密码是否正确");
                report.addSuggestion("确认用户是否有ONVIF访问权限");
                report.addSuggestion("尝试通过设备Web界面验证认证信息");
                report.addSuggestion("检查设备是否启用了特殊的认证模式");
                return false;
            }
            
            return true;
            
        } catch (Exception e) {
            log.warn("认证测试失败: {}", e.getMessage());
            report.setResult(DiagnosticResult.AUTH_FAILED);
            report.setErrorMessage("认证测试异常: " + e.getMessage());
            report.addSuggestion("检查认证配置是否正确");
            return false;
        }
    }
    
    /**
     * 测试Basic认证
     */
    private boolean testBasicAuth(OnvifDevice device, DiagnosticReport report) {
        try {
            String soapRequest = buildGetCapabilitiesRequest();
            String serviceUrl = "http://" + device.getIpAddress() + ":" + device.getPort() + "/onvif/device_service";
            
            String response = sendHttpRequest(serviceUrl, soapRequest, device.getUsername(), device.getPassword());
            
            if (response != null && response.contains("GetCapabilitiesResponse")) {
                report.addDetail("Basic认证", "成功");
                return true;
            } else {
                report.addDetail("Basic认证", "失败");
                return false;
            }
            
        } catch (Exception e) {
            report.addDetail("Basic认证", "异常: " + e.getMessage());
            return false;
        }
    }
    
    /**
     * 测试Digest认证
     */
    private boolean testDigestAuth(OnvifDevice device, DiagnosticReport report) {
        try {
            // 这里可以集成现有的Digest认证逻辑
            // 由于复杂性，这里简化处理
            report.addDetail("Digest认证", "需要集成现有认证管理器");
            return false;
            
        } catch (Exception e) {
            report.addDetail("Digest认证", "异常: " + e.getMessage());
            return false;
        }
    }
    
    /**
     * 测试通道信息获取
     */
    private boolean testChannelInfoRetrieval(OnvifDevice device, DiagnosticReport report) {
        log.debug("测试通道信息获取: {}", device.getIpAddress());
        
        try {
            // 先获取设备的配置文件列表
            List<OnvifClientService.ProfileInfo> profiles = onvifClientService.getProfiles(device);
            if (profiles == null || profiles.isEmpty()) {
                report.addDetail("通道信息获取", "失败 - 无法获取配置文件");
                return false;
            }
            
            // 使用第一个配置文件测试通道配置获取
            String profileToken = profiles.get(0).getToken();
            Map<String, Object> channelConfig = onvifClientService.getChannelConfiguration(device, profileToken);
            
            if (channelConfig != null && !channelConfig.isEmpty()) {
                report.addDetail("通道信息获取", "成功");
                report.addDetail("通道数量", channelConfig.size());
                return true;
            } else {
                report.addDetail("通道信息获取", "失败 - 无通道数据");
                report.addSuggestion("检查设备是否配置了视频通道");
                report.addSuggestion("确认用户权限是否包含媒体服务访问");
                return false;
            }
            
        } catch (Exception e) {
            log.warn("通道信息获取测试失败: {}", e.getMessage());
            report.addDetail("通道信息获取", "异常: " + e.getMessage());
            
            // 分析具体错误类型
            if (e.getMessage().contains("401")) {
                report.addSuggestion("401认证失败 - 检查媒体服务的用户权限");
            } else if (e.getMessage().contains("timeout")) {
                report.addSuggestion("连接超时 - 增加超时时间或检查网络稳定性");
            } else if (e.getMessage().contains("Connection reset")) {
                report.addSuggestion("连接重置 - 设备可能负载过高或网络不稳定");
            }
            
            return false;
        }
    }
    
    /**
     * 构建GetCapabilities SOAP请求
     */
    private String buildGetCapabilitiesRequest() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
               "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\">" +
               "<soap:Body>" +
               "<tds:GetCapabilities xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\"/>" +
               "</soap:Body>" +
               "</soap:Envelope>";
    }
    
    /**
     * 发送HTTP请求
     */
    private String sendHttpRequest(String url, String soapRequest, String username, String password) throws IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/soap+xml; charset=utf-8");
        connection.setConnectTimeout(2000);
        connection.setReadTimeout(2000);
        connection.setDoOutput(true);
        
        // 添加Basic认证
        if (username != null && password != null) {
            String auth = username + ":" + password;
            String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
            connection.setRequestProperty("Authorization", "Basic " + encodedAuth);
        }
        
        // 发送请求
        try (var outputStream = connection.getOutputStream()) {
            outputStream.write(soapRequest.getBytes("UTF-8"));
        }
        
        // 读取响应
        int responseCode = connection.getResponseCode();
        if (responseCode == 200 || responseCode == 401) {
            try (var inputStream = (responseCode == 200) ? connection.getInputStream() : connection.getErrorStream();
                 var scanner = new Scanner(inputStream, "UTF-8")) {
                return scanner.useDelimiter("\\A").hasNext() ? scanner.next() : "";
            }
        }
        
        return null;
    }
    
    /**
     * 生成修复建议
     */
    public List<String> generateFixSuggestions(DiagnosticReport report) {
        List<String> suggestions = new ArrayList<>();
        
        switch (report.getResult()) {
            case AUTH_FAILED:
                suggestions.add("更新application-device-config.yml中的认证超时配置");
                suggestions.add("尝试使用Basic认证替代Digest认证");
                suggestions.add("检查设备用户权限设置");
                break;
                
            case TIMEOUT:
                suggestions.add("增加连接超时时间到10秒以上");
                suggestions.add("检查网络延迟和带宽");
                suggestions.add("启用连接重试机制");
                break;
                
            case NETWORK_UNREACHABLE:
                suggestions.add("检查设备网络配置");
                suggestions.add("验证路由和防火墙设置");
                suggestions.add("确认设备IP地址是否正确");
                break;
                
            default:
                suggestions.add("查看详细日志获取更多信息");
                break;
        }
        
        return suggestions;
    }
}